package com.gaga.metadata.model;

import cn.hutool.core.collection.CollectionUtil;
import com.gaga.metadata.common.assertion.Asserts;
import com.gaga.metadata.common.enums.DbDDLType;
import com.gaga.metadata.common.utils.StringUtils;
import com.gaga.metadata.convert.IAggTypeConvert;
import com.gaga.metadata.convert.IDataTypeConvert;
import com.gaga.metadata.convert.IFilterConvert;
import com.gaga.metadata.ddl.ITableDDL;
import com.gaga.metadata.driver.AbstractJdbcDriver;
import com.gaga.metadata.entity.database.*;
import com.gaga.metadata.function.AbstractFunctionFactory;
import com.gaga.metadata.query.IDBQuery;
import com.gaga.metadata.utils.SqlStrUtils;
import freemarker.template.Template;

import java.io.StringWriter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 关于database相关的model Provider
 *
 * @Date 2023/4/24 15:58
 * @Author zhipeng.yang
 **/
public abstract class AbstractDbModelProvider extends AbstractModelProvider<DataBaseTable> {


    protected IDataTypeConvert dataTypeConvert;

    protected IAggTypeConvert aggTypeConvert;
    protected IFilterConvert filterConvert;

    protected ITableDDL tableDDL;

    protected AbstractJdbcDriver driver;

    protected IDBQuery idbQuery;

    protected AbstractFunctionFactory abstractFunctionFactory;


    public void init(AbstractJdbcDriver driver) {
        this.driver = driver;
        this.dataTypeConvert = driver.getDataTypeConvert();
        this.filterConvert = driver.getFilterConvert();
        this.aggTypeConvert = driver.getAggTypeConvert();
        this.tableDDL = driver.getTableDDL();
        this.idbQuery = driver.getDBQuery();
        abstractFunctionFactory = AbstractFunctionFactory.init(driver.getAbstractFunctionFactory());
    }

    /**
     * 塞入addColumn时候的
     *
     * @param table
     * @return
     */
    protected Map<String, Object> setAddColumnTemplateMap(DataBaseTable table) {
        Map<String, Object> templateMap = new HashMap();
        table.getColumns().forEach(dataBaseColumn -> dataBaseColumn.setColumnFullInfo(dataTypeConvert.getColumnFullInfo(dataBaseColumn)));
        templateMap.put("columnList", table.getColumns());
        templateMap.put("tableName", table.getTableName());
        return templateMap;
    }

    /**
     * 塞入AddIndex
     *
     * @param table
     * @return
     */
    protected Map<String, Object> setAddIndexTemplateMap(DataBaseTable table) {
        Map<String, Object> templateMap = new HashMap();

        if (CollectionUtil.isNotEmpty(table.getIndexs())) {
            table.getIndexs().forEach(dataBaseIndex -> dataBaseIndex.setFullColumnInfo(dataBaseIndex.getColumnList().stream().map(DataBaseColumn::getName).collect(Collectors.joining(","))));
            templateMap.put("indexList", table.getIndexs());
        }

        templateMap.put("tableName", table.getTableName());
        return templateMap;
    }

    /**
     * 塞入updateColumn时候的
     *
     * @param table
     * @return
     */
    protected Map<String, Object> setUpdateColumnTemplateMap(DataBaseTable table) {
        Map<String, Object> templateMap = new HashMap();
        table.getColumns().forEach(dataBaseColumn -> dataBaseColumn.setColumnFullInfo(dataTypeConvert.getColumnTypeFullInfo(dataBaseColumn)));
        templateMap.put("columnList", table.getColumns());
        templateMap.put("tableName", table.getTableName());
        return templateMap;
    }


    /**
     * 塞入updateColumn时候的
     *
     * @param table
     * @return
     */
    protected Map<String, Object> setUpdateTableTemplateMap(DataBaseTable table) {
        Map<String, Object> templateMap = new HashMap();
        templateMap.put("table", table);
        return templateMap;
    }


    /**
     * 塞入delIndex时候的
     *
     * @param table
     * @return
     */
    protected Map<String, Object> setDelIndexTemplateMap(DataBaseTable table) {
        Map<String, Object> templateMap = new HashMap();
        templateMap.put("indexList", table.getIndexs());
        return templateMap;
    }

    /**
     * 塞入delColumn时候的
     *
     * @param table
     * @return
     */
    protected Map<String, Object> setDelColumnTemplateMap(DataBaseTable table) {
        Map<String, Object> templateMap = new HashMap();
        templateMap.put("columnList", table.getColumns());
        templateMap.put("tableName", table.getTableName());
        return templateMap;
    }

    /**
     * 塞入createTable时候的
     *
     * @param table
     * @return
     */
    protected Map<String, Object> setCreateTableTemplateMap(DataBaseTable table) {
        //需要先过滤一下column 然后把主键放到前面去
        table.setColumns(table.getColumns().stream().sorted(Comparator.comparing(DataBaseColumn::getKeyFlag).reversed())
                .collect(Collectors.toList()));
        table.getColumns().forEach(dataBaseColumn ->
                dataBaseColumn.setColumnFullInfo(dataTypeConvert.getColumnFullInfo(dataBaseColumn)));
        Map<String, Object> templateMap = new HashMap();
        templateMap.put("columnList", table.getColumns());

        if (CollectionUtil.isNotEmpty(table.getIndexs())) {
            table.getIndexs().forEach(dataBaseIndex -> dataBaseIndex.setFullColumnInfo(dataBaseIndex.getColumnList().stream().map(DataBaseColumn::getName).collect(Collectors.joining(","))));
            templateMap.put("indexList", table.getIndexs());
        }
        String distributedKeys = table.getColumns().stream().filter(DataBaseColumn::getIsDistributed).map(DataBaseColumn::getName)
                .collect(Collectors.joining(","));
        templateMap.put("distributedKeys", distributedKeys);

        templateMap.put("primayKeyList", getPrimayKeyList(table.getColumns()));
        templateMap.put("tableName", table.getTableName());
        templateMap.put("tableComment", table.getComment());
        return templateMap;
    }

    /**
     * 塞入select时候的
     *
     * @param table
     * @return
     */
    protected Map<String, Object> setSelectTemplateMap(DataBaseTable table) {
        Map<String, Object> templateMap = new HashMap();
        templateMap.put("whereCondition", table.getWhereCondition());
        templateMap.put("columnList", table.getColumns());
        templateMap.put("tableName", table.getSchemaTableName());
        templateMap.put("aliasName", table.getAliasName());
        templateMap.put("filterList", table.getFilters());
        templateMap.put("groupList", table.getGroupBys());
        templateMap.put("orderList", table.getOrderBys());
        templateMap.put("joinList", table.getJoins());
        if (Asserts.isNotNull(table.getPage())) {
            String firstIndex = String.valueOf((table.getPage().getPageNum() - 1) * table.getPage().getPageSize());
            String total = String.valueOf(table.getPage().getPageSize());
            templateMap.put("firstIndex", firstIndex);
            templateMap.put("pageSize", total);
        }
        return templateMap;
    }

    /**
     * 获取主键信息
     *
     * @param columnList
     * @return
     */
    protected List<String> getPrimayKeyList(List<DataBaseColumn> columnList) {
        if (Asserts.isNotNullCollection(columnList)) {
            columnList = columnList.stream().sorted(Comparator.comparing(DataBaseColumn::getKeyFlag).reversed())
                    .collect(Collectors.toList());
            return columnList.stream().filter(cloumn -> cloumn.getKeyFlag())
                    .map(cloumn -> cloumn.getName().toLowerCase()).collect(Collectors.toList());
        }
        return null;
    }

    /**
     * 通过ddlType直接获取生成的sql
     *
     * @param ddlType
     * @return
     */
    protected String getGenSqlByDdlType(Map<String, Object> templateMap, DbDDLType ddlType) {
        Template template;
        try {
            template = buildFreemarkerTemplate(driver.getType() + "_" + ddlType.getTemplateName() + ".ftl");
            StringWriter stringWriter = new StringWriter();
            template.process(templateMap, stringWriter);
            String resultStr = stringWriter.toString();
            resultStr = SqlStrUtils.replaceSql(resultStr);
            return resultStr;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    protected void handlerSqlFunctionType(DataBaseTable table) {
        //对度量列和维度列进行特殊处理
        if (CollectionUtil.isNotEmpty(table.getColumns())) {
            table.getColumns().forEach(dataBaseColumn -> {
                if (dataBaseColumn.getSqlFunctionType() != null) {
                    List<DataBaseOption> optionsResult = new ArrayList<>();
                    List<DataBaseFilter> optionFilterList = abstractFunctionFactory.createFunctionProvider(dataBaseColumn.getSqlFunctionType()).handler(dataBaseColumn);
                    if (CollectionUtil.isNotEmpty(optionFilterList) && optionFilterList.size() > 0) {
                        DataBaseOption baseColumnOption = new DataBaseOption();
                        baseColumnOption.setFilters(optionFilterList);
                        optionsResult.add(baseColumnOption);
                        dataBaseColumn.setOptions(optionsResult);
                    }
                }
            });
        }
    }

    protected void handlerGroupBys(DataBaseTable table) {

        if (CollectionUtil.isNotEmpty(table.getColumns())) {
            List<DataBaseGroupBy> calcEngineGroupByList = table.getColumns().stream()
                    .filter(dataBaseColumn -> dataBaseColumn.getNeedGroupBy())
                    .map(dataBaseColumn -> {
                        DataBaseGroupBy dataBaseGroupBy = new DataBaseGroupBy();
                        dataBaseGroupBy.setColumnName(dataBaseColumn.getName());
                        dataBaseGroupBy.setLinkTableNameOrAliasName(dataBaseColumn.getLinkTableNameOrAliasName());

                        List<DataBaseOption> optionsResult = new ArrayList<>();

                        //如果是维度是时间，即统计周期有值得话，则将group by 后的维度列进行处理
                        if (dataBaseColumn.getStatisticPeriod() != null) {
                            //处理groupby中的 to_char()等函数
                            List<DataBaseFilter> optionFilterList = abstractFunctionFactory.createFunctionProvider(dataBaseColumn.getSqlFunctionType()).handler(dataBaseColumn);

                            if (CollectionUtil.isNotEmpty(optionFilterList) && optionFilterList.size() > 0) {
                                DataBaseOption baseColumnOption = new DataBaseOption();
                                baseColumnOption.setFilters(optionFilterList);
                                optionsResult.add(baseColumnOption);
                                dataBaseGroupBy.setOptions(optionsResult);
                            }
                        }
                        return dataBaseGroupBy;
                    })
                    .collect(Collectors.toList());
            table.setGroupBys(calcEngineGroupByList);
        }
    }

    /**
     * 处理filter
     *
     * @param table
     */
    protected void handlerWhereCondition(DataBaseTable table) {
        if (table.getWhereCondition() != null) {
            DataBaseWhere dataBaseWhere = table.getWhereCondition();
            if (CollectionUtil.isNotEmpty(dataBaseWhere.getOptions())) {

                for (DataBaseOption option : dataBaseWhere.getOptions()) {
                    List<DataBaseFilter> filters = new ArrayList<>();
                    if (option.getType() == 1) {
                        filters.addAll(option.getFilters());
                    }
                    option.setFilters(filters);
                    for (DataBaseFilter filter : filters) {
                        filterConvert.convertFilterAll(filter);
                    }
                }
                //转换

            }
        }
    }

    /**
     * 处理orderBy
     *
     * @param table
     */
    protected void handlerOrderBys(DataBaseTable table) {
        if (CollectionUtil.isNotEmpty(table.getColumns())) {
            List<DataBaseOrderBy> calcEngineOrderByList = table.getColumns().stream().
                    filter(dataBaseColumn -> StringUtils.isNotEmpty(dataBaseColumn.getCollation()))
                    .map(dataBaseColumn -> {
                        DataBaseOrderBy dataBaseOrderBy = new DataBaseOrderBy();
                        dataBaseOrderBy.setColumnName(dataBaseColumn.getName());
                        dataBaseOrderBy.setLinkTableNameOrAliasName(dataBaseColumn.getLinkTableNameOrAliasName());
                        dataBaseOrderBy.setAscOrDesc(dataBaseColumn.getCollation());
                        return dataBaseOrderBy;
                    }).collect(Collectors.toList());
            table.setOrderBys(calcEngineOrderByList);
        }
    }


}
